package net.sansi.v3correctserver.util;

import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.entity.IntPoint;
import net.sansi.v3correctserver.entity.color.PhotoColor;
import net.sansi.v3correctserver.entity.image.RecCmmImage;
import net.sansi.v3correctserver.entity.image.RecImage;
import net.sansi.v3correctserver.entity.screen.Rect;

import java.util.List;

/**
 * Created by Fang
 * create time : 2024/11/4
 * IDE : IntelliJ IDEA
 * <p>
 * 矫正工具，用于计算扩展列等
 */
@Slf4j
public class CorrectUtil {

    /**
     * 将百分比的矫正结果转换为基于基准亮度的亮度
     *
     * @param percent    百分比
     * @param baseBright 基准亮度
     * @return 亮度
     */
    public static PhotoColor percent2Bright(double[] percent, int baseBright) {
        return new PhotoColor(
                percent[0] * baseBright,
                percent[1] * baseBright,
                percent[2] * baseBright
        );
    }

    /**
     * 计算打屏应该识别到的宽高
     *
     * @param showWidth  打屏宽度
     * @param showHeight 打屏高度
     * @param span       隔点
     * @param ofx        offsetX
     * @param ofy        offsetY
     * @return 识别区域
     */
    public static Rect computeRecSize(int showWidth, int showHeight, int span, int ofx, int ofy) {
        return new Rect()
                .setWidth((showWidth - ofx + span - 1) / span)
                .setHeight((showHeight - ofy + span - 1) / span);
    }

    /**
     * 是否在区域内
     *
     * @param x     横坐标
     * @param y     纵坐标
     * @param fence 区域
     * @return 是否在区域内
     */
    public static boolean isInFence(int x, int y, List<IntPoint> fence) {
        int n = fence.size();
        if (n < 3) return false;
        int intersections = 0;
        for (int i = 0; i < n; i++) {
            IntPoint p1 = fence.get(i);
            IntPoint p2 = fence.get((i + 1) % n);
            int x1 = p1.getX(), x2 = p2.getX();
            int y1 = p1.getY(), y2 = p2.getY();
            if (y1 == y2 && y1 == y && x > Math.min(x1, x2) && x < Math.max(x1, x2)) return true;
            if (y1 < y && y2 >= y || y2 < y && y1 >= y) {
                double vt = (double) (y - y1) / (y2 - y1);
                if (x < x1 + vt * (x2 - x1)) {
                    intersections++;
                }
            }
        }
        return intersections % 2 == 1;
    }

    /**
     * 将 x y 转换为 long
     *
     * @param x x
     * @param y y
     * @return long
     */
    public static long xy2Long(int x, int y) {
        return ((long) x << 32) | (y & 0xFFFFFFFFL);
    }

    /**
     * 将 long 转换为 x y
     *
     * @param xy xy
     * @return x y
     */
    public static int[] long2Xy(long xy) {
        return new int[]{(int) (xy >> 32), (int) xy};
    }

    /**
     * 将 matrix 转换为图片
     * item 为亮度百分比
     *
     * @param matrix     matrix
     * @param colorIdx   颜色索引
     * @param baseBright 基准亮度（128）
     * @return 图片
     */
    public static RecCmmImage result2Preview(Matrix<Double> matrix, int colorIdx, int baseBright) {
        int w = matrix.getWidth(), h = matrix.getHeight();
        RecCmmImage image = new RecCmmImage(w, h);
        matrix.foreach((mx, my, item) -> {
            image.setColor(mx, my, new PhotoColor(item * baseBright, colorIdx));
        });
        return image;
    }

    /**
     * 预览图（result） 转 show
     *
     * @param previewImg    result
     * @param screenWidth   显示屏宽度
     * @param screenHeight  显示屏高度
     * @param screenOffsetX x 偏移
     * @param screenOffsetY y 偏移
     * @return show图片
     */
    public static RecCmmImage preview2Show(RecImage previewImg, int screenWidth, int screenHeight, int screenOffsetX,
                                           int screenOffsetY) {
        RecCmmImage res = new RecCmmImage(screenWidth, screenHeight);
        int w = previewImg.getWidth(), h = previewImg.getHeight();
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                res.setColor(x + screenOffsetX, y + screenOffsetY, previewImg.getPhotoColor(x, y));
            }
        }
        return res;
    }

    /**
     * show 转 预览图（result）
     *
     * @param showImg       打屏图片
     * @param previewWidth  result 宽度
     * @param previewHeight result 高度
     * @param offsetX       x 偏移
     * @param offsetY       y 偏移
     * @return 打屏结果图片
     */
    public static RecCmmImage show2Preview(RecImage showImg, int previewWidth, int previewHeight, int offsetX,
                                           int offsetY) {
        RecCmmImage res = new RecCmmImage(previewWidth, previewHeight);
        for (int x = 0; x < previewWidth; x++) {
            for (int y = 0; y < previewHeight; y++) {
                res.setColor(x, y, showImg.getPhotoColor(x - offsetX, y - offsetY));
            }
        }
        return res;
    }

    /**
     * result 转 preview
     *
     * @param result     结果
     * @param brightness 基准亮度
     * @return preview
     */
    public static RecCmmImage result2Preview(Matrix<double[]> result, int brightness) {
        int w = result.getWidth(), h = result.getHeight();
        RecCmmImage preview = new RecCmmImage(w, h);
        preview.setBackgroundColor(new PhotoColor(0, 0, 0));
        result.foreach((mx, my, item) -> {
            if (item == null) {
                preview.setColor(mx, my, new PhotoColor(brightness, brightness, brightness));
            } else {
                preview.setColor(mx, my, new PhotoColor(
                                (int) Math.round(item[0] * brightness),
                                (int) Math.round(item[1] * brightness),
                                (int) Math.round(item[2] * brightness)
                        )
                );
            }
        });
        return preview;
    }

    /**
     * result 转一帧
     *
     * @param result  result
     * @param span    隔点数量
     * @param offsetX x 偏移
     * @param offsetY y 偏移
     * @return 结果帧
     */
    public static Matrix<double[]> result2OneFrame(Matrix<double[]> result, int span, int offsetX, int offsetY) {
        Rect rec = computeRecSize(result.getWidth(), result.getHeight(), span, offsetX, offsetY);
        Matrix<double[]> matrix = new Matrix<>(rec.getWidth(), rec.getHeight());
        for (int x = 0; x < matrix.getWidth(); x++) {
            for (int y = 0; y < matrix.getHeight(); y++) {
                matrix.set(x, y, result.get(
                        x * span + offsetX,
                        y * span + offsetY
                ));
            }
        }
        return matrix;
    }

    /**
     * result 转颜色矩阵
     *
     * @param result   result
     * @param colorIdx 颜色索引
     * @return 一种颜色的结果系数
     */
    public static Matrix<Double> result2ColorMatrix(Matrix<double[]> result, int colorIdx) {
        Matrix<Double> matrix = new Matrix<>(result.getWidth(), result.getHeight());
        for (int x = 0; x < result.getWidth(); x++) {
            for (int y = 0; y < result.getHeight(); y++) {
                double[] c = result.get(x, y);
                matrix.set(x, y, c == null ? 0 : c[colorIdx]);
            }
        }
        return matrix;
    }

    /**
     * 亮度结果转色度结果
     *
     * @param result   亮度结果
     * @param colorIdx 颜色索引
     * @return 色度结果
     */
    public static Matrix<double[]> brightResult2ChromaResult(Matrix<Double> result, int colorIdx) {
        Matrix<double[]> matrix = new Matrix<>(result.getWidth(), result.getHeight(), (x, y) -> new double[]{0, 0, 0});
        result.foreach((x, y, value) -> {
            matrix.get(x, y)[colorIdx] = value;
        });
        return matrix;
    }

    /**
     * 从result中提取某一种颜色的值
     *
     * @param result   矫正结果
     * @param colorIdx 颜色索引
     * @return 单色矫正系数
     */
    public static Matrix<double[]> pickColorFromResult(Matrix<double[]> result, int colorIdx) {
        if (colorIdx == 3) return result;
        Matrix<double[]> matrix = new Matrix<>(result.getWidth(), result.getHeight());
        for (int x = 0; x < result.getWidth(); x++) {
            for (int y = 0; y < result.getHeight(); y++) {
                double[] c = result.get(x, y);
                if (c == null) c = new double[]{0, 0, 0};
                if (colorIdx == 0) c = new double[]{c[0], 0, 0};
                else if (colorIdx == 1) c = new double[]{0, c[1], 0};
                else if (colorIdx == 2) c = new double[]{0, 0, c[2]};
                matrix.set(x, y, c);
            }
        }
        return matrix;
    }
}
